package com.tangxz.comparison.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Description:
 * User: Administrator
 * Date: 2025/3/2 21:31
 */
@Component
public class ResultComparator {

    private static final Logger logger = LoggerFactory.getLogger(ResultComparator.class);

    @Async
    public void compareAsync(Object target, String methodName, Object[] args, Object aResult) {
        try {
            Object bResult = invokeTargetMethod(target, methodName, args);
            boolean isEqual = compareResults(aResult, bResult);
            logComparison(args, aResult, bResult, isEqual);
        } catch (Exception e) {
            logger.error("Comparison failed for method {}: {}", methodName, e.getMessage());
        }
    }

    private Object invokeTargetMethod(Object target, String methodName, Object[] args) throws Exception {
        Class<?> targetClass = target != null ? target.getClass() : getClassForStaticMethod(methodName);
        Method method = findBestMatchingMethod(targetClass, methodName, args);
        method.setAccessible(true);
        return target != null ?
                method.invoke(target, args) :
                method.invoke(null, args);
    }

    private Class<?> getClassForStaticMethod(String methodPath) throws ClassNotFoundException {
        String className = methodPath.substring(0, methodPath.lastIndexOf('.'));
        return Class.forName(className);
    }

    private Method findBestMatchingMethod(Class<?> targetClass, String methodName, Object[] args)
            throws NoSuchMethodException {

        List<Method> candidates = Arrays.stream(targetClass.getDeclaredMethods())
                .filter(m -> m.getName().equals(methodName))
                .filter(m -> m.getParameterCount() == args.length)
                .collect(Collectors.toList());

        if (candidates.isEmpty()) {
            throw new NoSuchMethodException(String.format(
                    "Method %s with %d parameters not found in %s",
                    methodName, args.length, targetClass.getName()));
        }

        // 精确匹配参数类型
        for (Method method : candidates) {
            if (isParameterMatch(method.getParameterTypes(), args)) {
                return method;
            }
        }

        // 宽松匹配（允许子类）
        for (Method method : candidates) {
            if (isLooseParameterMatch(method.getParameterTypes(), args)) {
                return method;
            }
        }

        throw new NoSuchMethodException("No matching parameter types for method: " + methodName);
    }

    private boolean isParameterMatch(Class<?>[] paramTypes, Object[] args) {
        for (int i = 0; i < paramTypes.length; i++) {
            if (args[i] != null && !paramTypes[i].isInstance(args[i])) {
                return false;
            }
        }
        return true;
    }

    private boolean isLooseParameterMatch(Class<?>[] paramTypes, Object[] args) {
        for (int i = 0; i < paramTypes.length; i++) {
            if (args[i] != null &&
                    !paramTypes[i].isAssignableFrom(args[i].getClass())) {
                return false;
            }
        }
        return true;
    }

    private boolean compareResults(Object aResult, Object bResult) {
        if (aResult == null) return bResult == null;
        return aResult.equals(bResult);
    }

    public void logComparison(Object[] args, Object aResult, Object bResult, boolean isEqual) {
        System.out.println("\n=== Method Comparison Result ===");
        System.out.println("Arguments: " + Arrays.toString(args));
        System.out.println("MethodA Result: " + aResult);
        System.out.println("MethodB Result: " + bResult);
        System.out.println("Results Match: " + (isEqual ? "✅" : "❌"));
        System.out.println("===============================\n");
    }
}